// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2014-2015, Antmicro Ltd <www.antmicro.com>
 * Copyright (c) 2015, AW-SOM Technologies <www.aw-som.com>
 */


#include "boot0_i.h"
#include "types.h"
#include "init_dram/dram_sun4i.h"
#include "init_dram/clock_sun4i.h"

#define __ALIGN_MASK(x,mask)	(((x)+(mask))&~(mask))
#define ALIGN(x,a)		__ALIGN_MASK((x),(typeof(x))(a)-1)

#define ARRAY_SIZE(arr)		(sizeof(arr) / sizeof((arr)[0]))
// #define ALIGN(addr, size) (((addr)+(size)-1)&(~((size)-1)))

#define SUNXI_PIO_BASE			0x01c28000
#define SUNXI_NFC_BASE			0x01c03000

/* registers */
#define NFC_CTL                    0x00000000
#define NFC_ST                     0x00000004
#define NFC_INT                    0x00000008
#define NFC_TIMING_CTL             0x0000000C
#define NFC_TIMING_CFG             0x00000010
#define NFC_ADDR_LOW               0x00000014
#define NFC_ADDR_HIGH              0x00000018
#define NFC_SECTOR_NUM             0x0000001C
#define NFC_CNT                    0x00000020
#define NFC_CMD                    0x00000024
#define NFC_RCMD_SET               0x00000028
#define NFC_WCMD_SET               0x0000002C
#define NFC_IO_DATA                0x00000030
#define NFC_ECC_CTL                0x00000034
#define NFC_ECC_ST                 0x00000038
#define NFC_DEBUG                  0x0000003C
#define NFC_ECC_CNT0               0x00000040
#define NFC_ECC_CNT1               0x00000044
#define NFC_ECC_CNT2               0x00000048
#define NFC_ECC_CNT3               0x0000004C
#define NFC_USER_DATA_BASE         0x00000050
#define NFC_EFNAND_STATUS          0x00000090
#define NFC_SPARE_AREA             0x000000A0
#define NFC_PATTERN_ID             0x000000A4
#define NFC_RAM0_BASE              0x00000400
#define NFC_RAM1_BASE              0x00000800

#define NFC_CTL_EN                 (1 << 0)
#define NFC_CTL_RESET              (1 << 1)
#define NFC_CTL_RAM_METHOD         (1 << 14)
#define NFC_CTL_PAGE_SIZE_MASK     (0xf << 8)
#define NFC_CTL_PAGE_SIZE(a)       ((fls(a) - 11) << 8)


#define NFC_ECC_EN                 (1 << 0)
#define NFC_ECC_PIPELINE           (1 << 3)
#define NFC_ECC_EXCEPTION          (1 << 4)
#define NFC_ECC_BLOCK_SIZE         (1 << 5)
#define NFC_ECC_RANDOM_EN          (1 << 9)
#define NFC_ECC_RANDOM_DIRECTION   (1 << 10)


#define NFC_ADDR_NUM_OFFSET        16
#define NFC_SEND_ADDR              (1 << 19)
#define NFC_ACCESS_DIR             (1 << 20)
#define NFC_DATA_TRANS             (1 << 21)
#define NFC_SEND_CMD1              (1 << 22)
#define NFC_WAIT_FLAG              (1 << 23)
#define NFC_SEND_CMD2              (1 << 24)
#define NFC_SEQ                    (1 << 25)
#define NFC_DATA_SWAP_METHOD       (1 << 26)
#define NFC_ROW_AUTO_INC           (1 << 27)
#define NFC_SEND_CMD3              (1 << 28)
#define NFC_SEND_CMD4              (1 << 29)
#define NFC_RAW_CMD                (0 << 30)
#define NFC_ECC_CMD                (1 << 30)
#define NFC_PAGE_CMD               (2 << 30)

#define NFC_ST_CMD_INT_FLAG        (1 << 1)
#define NFC_ST_DMA_INT_FLAG        (1 << 2)
#define NFC_ST_CMD_FIFO_STAT       (1 << 3)

#define NFC_RB_STATE0			(1 << 8)

#define NFC_READ_CMD_OFFSET         0
#define NFC_RANDOM_READ_CMD0_OFFSET 8
#define NFC_RANDOM_READ_CMD1_OFFSET 16

#define NFC_CMD_RNDOUTSTART        0xE0
#define NFC_CMD_RNDOUT             0x05
#define NFC_CMD_READSTART          0x30


#define NAND_CMD_RESET		0xff

#define ETIMEDOUT 2
#define EIO 3
#define EINVAL 4
#define true 1
#define false 0

struct nfc_config {
	int page_size;
	int ecc_strength;
	int ecc_size;
	int addr_cycles;
	int nseeds;
	bool randomize;
	bool valid;
};

/* minimal "boot0" style NAND support for Allwinner A20 */

/* random seed used by linux */
const uint16_t random_seed[128] = {
	0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
	0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
	0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
	0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
	0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
	0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
	0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
	0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
	0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
	0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
	0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
	0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
	0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
	0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
	0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
	0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
};

__s32  nand_get_id();
static inline int fls(int x)
{
	int r = 32;

	if (!x)
		return 0;
	if (!(x & 0xffff0000u)) {
		x <<= 16;
		r -= 16;
	}
	if (!(x & 0xff000000u)) {
		x <<= 8;
		r -= 8;
	}
	if (!(x & 0xf0000000u)) {
		x <<= 4;
		r -= 4;
	}
	if (!(x & 0xc0000000u)) {
		x <<= 2;
		r -= 2;
	}
	if (!(x & 0x80000000u)) {
		x <<= 1;
		r -= 1;
	}
	return r;
}

#define DEFAULT_TIMEOUT_US	100000

static int check_value_inner(int offset, int expected_bits,
			     int timeout_us, int negation)
{
	do {
		int val = readl(offset) & expected_bits;
		if (negation ? !val : val)
			return 1;
		udelay(1);
	} while (--timeout_us);

	return 0;
}

static inline int check_value(int offset, int expected_bits,
			      int timeout_us)
{
	return check_value_inner(offset, expected_bits, timeout_us, 0);
}

static inline int check_value_negated(int offset, int unexpected_bits,
				      int timeout_us)
{
	return check_value_inner(offset, unexpected_bits, timeout_us, 1);
}

int nand_wait_cmd_fifo_empty(void)
{
	if (!check_value_negated(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_FIFO_STAT,
				 DEFAULT_TIMEOUT_US)) {
		msg("nand: timeout waiting for empty cmd FIFO\n");
		return -ETIMEDOUT;
	}

	return 0;
}

int nand_wait_int(void)
{
	if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG,
			 DEFAULT_TIMEOUT_US)) {
		msg("nand: timeout waiting for interruption\n");
		return -ETIMEDOUT;
	}

	return 0;
}

static int nand_exec_cmd(u32 cmd)
{
	int ret;

	ret = nand_wait_cmd_fifo_empty();
	if (ret)
		return ret;

	writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
    msg("nand cmd: %x [%x]\n", cmd, SUNXI_NFC_BASE + NFC_CMD);
	writel(cmd, SUNXI_NFC_BASE + NFC_CMD);

	return nand_wait_int();
}
#define SUNXI_GPC_NAND		2
#define SUNXI_GPC(_nr)	(SUNXI_GPIO_C_START + (_nr))

static void nand_pinmux_setup(void)
{
	// unsigned int pin;

	// for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(19); pin++)
	// 	sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_NAND);

	// for (pin = SUNXI_GPC(20); pin <= SUNXI_GPC(22); pin++)
	// 	sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_NAND);

    // writel(0x22222222, SUNXI_PIO_BASE + 0x48);
    // writel(0x22222222, SUNXI_PIO_BASE + 0x4c);
    // writel(0x02222222, SUNXI_PIO_BASE + 0x50);
    *(volatile uint *)(0x01c20800 + 0x48) = 0x22222222;
    *(volatile uint *)(0x01c20800 + 0x4C) = 0x22222222;
    *(volatile uint *)(0x01c20800 + 0x50) = 0x2222222;
    *(volatile uint *)(0x01c20800 + 0x54) = 0x2;
}
#define CLK_GATE_OPEN			0x1
#define CLK_GATE_CLOSE			0x0
#define REGS_BASE	   0x01C00000		//寄存器物理地址
#define CCMU_REGS_BASE         ( REGS_BASE + 0x20000 )    //clock manager unit

static void nand_clock_setup(void)
{
    __u32 cfg;
	struct sunxi_ccm_reg *const ccm =
		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;

	setbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));

	/*set nand clock gate on*/
	// cfg = *(volatile __u32 *)(CCMU_REGS_BASE + 0x80);

	// /*gate on nand clock*/
	// cfg |= (1U << 31);
	// /*take cmu pll6 as nand src block*/
	// cfg &= ~(0x3 << 24);
	// cfg |=  (0x1 << 24);
	// //set divn = 0
	// cfg &= ~(0x03 << 16);

	// /*set ratio*/
	// cfg &= ~(0x0f << 0);
	// cfg |= 0xf << 0;

	// *(volatile __u32 *)(CCMU_REGS_BASE + 0x80) = cfg;

    setbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
}

void nand_init(void)
{
	uint32_t val,cfg,i, timeout = 0xffff;

	nand_pinmux_setup();
#if 1
	nand_clock_setup();
	val = readl(SUNXI_NFC_BASE + NFC_CTL);
	/* enable and reset CTL */
	writel(val | NFC_CTL_EN | NFC_CTL_RESET,
	       SUNXI_NFC_BASE + NFC_CTL);

	if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL,
				 NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) {
		msg("Couldn't initialize nand\n");
	}

	// /*set NFC_TIMING */
	// val = 0;
	// val |= 0x1f;
	// // val |= 0x2 << 8;
    // cfg |=((0x1) & 0xf)<<8;
	// writel(val, SUNXI_NFC_BASE + NFC_TIMING_CTL);
	// writel(0xff, SUNXI_NFC_BASE + NFC_TIMING_CFG);
	// /*set NFC_SPARE_AREA */
	// writel(640, SUNXI_NFC_BASE + NFC_SPARE_AREA);

    // /* reset NAND */
	// // nand_exec_cmd(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET);
	nand_exec_cmd(NFC_SEND_CMD1 | NAND_CMD_RESET);
    nfc_get_status();
    udelay(200);
    nand_get_id();
    udelay(200);
#endif
    // nand_read_test();
    nand_read_page_test();
    // nand_random_read_test();
    // nand_read_test3();
    // nand_spl_load_image(0, 2048, 0x40000000);
    // msg("%x ", readl(0x40000000 + i));
	// for (i = 0; i < 10; i++){
    //     msg("%x ", readl(0x40000000 + i * 4));
	// }
    // msg("%x ", readl(0x40000000 + i * 4));
}

static void nand_apply_config(const struct nfc_config *conf)
{
	u32 val;

	nand_wait_cmd_fifo_empty();

	val = readl(SUNXI_NFC_BASE + NFC_CTL);
	val &= ~NFC_CTL_PAGE_SIZE_MASK;
	writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size),
	       SUNXI_NFC_BASE + NFC_CTL);
	writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
	writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA);
}

static int nand_load_page(const struct nfc_config *conf, u32 offs)
{
	int page = offs / conf->page_size;

    msg("%s page:%d\n", __FUNCTION__, page);
	writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
	       (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
	       (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET),
	       SUNXI_NFC_BASE + NFC_RCMD_SET);
	writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW);
	writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH);
	return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
			     NFC_SEND_ADDR | NFC_WAIT_FLAG |
			     ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET));
}

static int nand_change_column(u16 column)
{
	int ret;

    msg("%s column:%d\n", __FUNCTION__, column);
	writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
	       (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
	       (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET),
	       SUNXI_NFC_BASE + NFC_RCMD_SET);
	writel(column, SUNXI_NFC_BASE + NFC_ADDR_LOW);

	ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
			    (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR |
			    NFC_CMD_RNDOUT);
	if (ret)
		return ret;

	/* Ensure tCCS has passed before reading data */
	udelay(1);

	return 0;
}

static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116};

static int nand_read_page(const struct nfc_config *conf, u32 offs,
			  void *dest, int len)
{
	int nsectors = len / conf->ecc_size;
	u16 rand_seed = 0;
	int oob_chunk_sz = ecc_bytes[conf->ecc_strength];
	int page = offs / conf->page_size;
	u32 ecc_st;
	int i;

	if (offs % conf->page_size || len % conf->ecc_size ||
	    len > conf->page_size || len < 0)
		return -EINVAL;

	/* Choose correct seed if randomized */
	if (conf->randomize)
		rand_seed = random_seed[page % conf->nseeds];
    if(page == 0)
		rand_seed = 0x4a80;

    msg("%s page_size:%d!\n", __FUNCTION__, conf->page_size);
    msg("%s rand_seed:%x!\n", __FUNCTION__, rand_seed);
	/* Retrieve data from SRAM (PIO) */
	for (i = 0; i < nsectors; i++) {
		int data_off = i * conf->ecc_size;
		int oob_off = conf->page_size + (i * oob_chunk_sz);
		u8 *data = dest + data_off;

		/* Clear ECC status and restart ECC engine */
		writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
		writel((rand_seed << 16) | (conf->ecc_strength << 12) |
		       (conf->randomize ? NFC_ECC_RANDOM_EN : 0) |
		       (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) |
		       NFC_ECC_EN | NFC_ECC_EXCEPTION,
		       SUNXI_NFC_BASE + NFC_ECC_CTL);

		/* Move the data in SRAM */
		nand_change_column(data_off);
		writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
		nand_exec_cmd(NFC_DATA_TRANS);

		/*
		 * Let the ECC engine consume the ECC bytes and possibly correct
		 * the data.
		 */
		nand_change_column(oob_off);
		nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD);

        print_nand_register();
		/* Get the ECC status */
		ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);

        msg("nand ecc_st:%x \n", ecc_st);
		/* ECC error detected. */
		if (ecc_st & 0xffff)
			return -EIO;

		/*
		 * Return 1 if the first chunk is empty (needed for
		 * configuration detection).
		 */
		if (!i && (ecc_st & 0x10000))
			return 1;
        msg("nand:%x \n", readl(SUNXI_NFC_BASE + NFC_RAM0_BASE));
		/* Retrieve the data from SRAM */
		memcpy(data, (void *)(SUNXI_NFC_BASE + NFC_RAM0_BASE),
			      conf->ecc_size);

		/* Stop the ECC engine */
		writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN,
		       SUNXI_NFC_BASE + NFC_ECC_CTL);

		if (data_off + conf->ecc_size >= len)
			break;
	}

	return 0;
}

static int nand_max_ecc_strength(struct nfc_config *conf)
{
	int max_oobsize, max_ecc_bytes;
	int nsectors = conf->page_size / conf->ecc_size;
	int i;

	/*
	 * ECC strength is limited by the size of the OOB area which is
	 * correlated with the page size.
	 */
	switch (conf->page_size) {
	case 2048:
		max_oobsize = 64;
		break;
	case 4096:
		max_oobsize = 256;
		break;
	case 8192:
		max_oobsize = 640;
		break;
	case 16384:
		max_oobsize = 1664;
		break;
	default:
		return -EINVAL;
	}

	max_ecc_bytes = max_oobsize / nsectors;

	for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) {
		if (ecc_bytes[i] > max_ecc_bytes)
			break;
	}

	if (!i)
		return -EINVAL;

	return i - 1;
}

static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs,
				  void *dest)
{
	/* NAND with pages > 4k will likely require 1k sector size. */
	int min_ecc_size = conf->page_size > 4096 ? 1024 : 512;
	int page = offs / conf->page_size;
	int ret;

	/*
	 * In most cases, 1k sectors are preferred over 512b ones, start
	 * testing this config first.
	 */
	for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size;
	     conf->ecc_size >>= 1) {
		int max_ecc_strength = nand_max_ecc_strength(conf);

		nand_apply_config(conf);

		/*
		 * We are starting from the maximum ECC strength because
		 * most of the time NAND vendors provide an OOB area that
		 * barely meets the ECC requirements.
		 */
		for (conf->ecc_strength = max_ecc_strength;
		     conf->ecc_strength >= 0;
		     conf->ecc_strength--) {
			conf->randomize = false;
			if (nand_change_column(0))
				return -EIO;

			/*
			 * Only read the first sector to speedup detection.
			 */
			ret = nand_read_page(conf, offs, dest, conf->ecc_size);
			if (!ret) {
                msg("%s 33!\n", __FUNCTION__);
				return 0;
			} else if (ret > 0) {
				/*
				 * If page is empty we can't deduce anything
				 * about the ECC config => stop the detection.
				 */
                msg("%s 22!\n", __FUNCTION__);
				return -EINVAL;
			}

            msg("%s 88!\n", __FUNCTION__);
			conf->randomize = true;
			conf->nseeds = ARRAY_SIZE(random_seed);
			do {
				if (nand_change_column(0))
                {
                    msg("%s %d!\n", __FUNCTION__, __LINE__);
                    return -EIO;
                }

				if (!nand_read_page(conf, offs, dest,
						    conf->ecc_size))
					return 0;

				/*
				 * Find the next ->nseeds value that would
				 * change the randomizer seed for the page
				 * we're trying to read.
				 */
				while (conf->nseeds >= 16) {
					int seed = page % conf->nseeds;

					conf->nseeds >>= 1;
					if (seed != page % conf->nseeds)
						break;
				}
			} while (conf->nseeds >= 16);
		}
	}

    msg("%s exit!\n", __FUNCTION__);
	return -EINVAL;
}

static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest)
{
    msg("%s 9\n", __FUNCTION__);
	if (conf->valid)
    {
        msg("%s valid!\n", __FUNCTION__);
		return 0;
    }

	/*
	 * Modern NANDs are more likely than legacy ones, so we start testing
	 * with 5 address cycles.
	 */
	for (conf->addr_cycles = 5;
	     conf->addr_cycles >= 4;
	     conf->addr_cycles--) {
		int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384;

		/*
		 * Ignoring 1k pages cause I'm not even sure this case exist
		 * in the real world.
		 */
		for (conf->page_size = 2048; conf->page_size <= max_page_size;
		     conf->page_size <<= 1) {
			if (nand_load_page(conf, offs))
				return -1;

			if (!nand_detect_ecc_config(conf, offs, dest)) {
				conf->valid = true;
				return 0;
			}
		}
	}

    msg("%s exit\n", __FUNCTION__);
	return -EINVAL;
}

static int nand_read_buffer(struct nfc_config *conf, uint32_t offs,
			    unsigned int size, void *dest)
{
	int first_seed = 0, page, ret;

	size = ALIGN(size, conf->page_size);
	page = offs / conf->page_size;
	if (conf->randomize)
		first_seed = page % conf->nseeds;

	for (; size; size -= conf->page_size) {
		if (nand_load_page(conf, offs))
			return -1;

		ret = nand_read_page(conf, offs, dest, conf->page_size);
		/*
		 * The ->nseeds value should be equal to the number of pages
		 * in an eraseblock. Since we don't know this information in
		 * advance we might have picked a wrong value.
		 */
		if (ret < 0 && conf->randomize) {
			int cur_seed = page % conf->nseeds;

			/*
			 * We already tried all the seed values => we are
			 * facing a real corruption.
			 */
			if (cur_seed < first_seed)
				return -EIO;

			/* Try to adjust ->nseeds and read the page again... */
			conf->nseeds = cur_seed;

			if (nand_change_column(0))
				return -EIO;

			/* ... it still fails => it's a real corruption. */
			if (nand_read_page(conf, offs, dest, conf->page_size))
				return -EIO;
		} else if (ret && conf->randomize) {
			memset(dest, 0xff, conf->page_size);
		}

		page++;
		offs += conf->page_size;
		dest += conf->page_size;
	}

	return 0;
}

int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
{
	static struct nfc_config conf = {0};
	int ret;

    memset(&conf, 0, sizeof(struct nfc_config));
	ret = nand_detect_config(&conf, offs, dest);
	if (ret)
    {
        msg("%s exit\n", __FUNCTION__);
		return ret;
    }
	return nand_read_buffer(&conf, offs, size, dest);
}

void nand_deselect(void)
{
	struct sunxi_ccm_reg *const ccm =
		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;

	clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
	clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
	clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
}


void nfc_repeat_mode_enable(void)
{
    __u32 reg_val;

    reg_val = readl(SUNXI_NFC_BASE + NFC_CTL);
	if(((reg_val>>18)&0x3)>1)   //ddr type
	{
    	reg_val |= 0x1<<20;
        writel(reg_val, SUNXI_NFC_BASE + NFC_CTL);
    }

}

void nfc_repeat_mode_disable(void)
{
    __u32 reg_val;

    reg_val = readl(SUNXI_NFC_BASE + NFC_CTL);
	if(((reg_val>>18)&0x3)>1)   //ddr type
	{
    	reg_val &= (~(0x1<<20));
        writel(reg_val, SUNXI_NFC_BASE + NFC_CTL);
    }
}

int nfc_set_id(void)
{
	__u32 cfg;
	int ret;

    cfg = 0;
    /*set addr*/
	writel(cfg, SUNXI_NFC_BASE + NFC_ADDR_LOW);
	writel(cfg, SUNXI_NFC_BASE + NFC_ADDR_HIGH);

    writel((readl(SUNXI_NFC_BASE + NFC_CTL)) & (~NFC_CTL_RAM_METHOD),
           SUNXI_NFC_BASE + NFC_CTL);
    /*set sequence mode*/
    //cfg |= 0x1<<25;
	writel(6, SUNXI_NFC_BASE + NFC_CNT);

	nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_ADDR | NFC_DATA_TRANS | 0x90);

    return 0;
}

int nfc_get_id(char *idbuf)
{
	__u32 i;
	int ret;

    nfc_repeat_mode_enable();
	ret = nfc_set_id();
	if (ret){
		return ret;
	}

	ret = nand_wait_cmd_fifo_empty();
	ret |= nand_wait_int();

	/*get 6 bytes id value*/
    /* Retrieve the data from SRAM */
    // memcpy(idbuf, (void *)(SUNXI_NFC_BASE + NFC_RAM0_BASE),
    //             6);
    msg("nand id [%x]: ", SUNXI_NFC_BASE + NFC_RAM0_BASE);
	for (i = 0; i < 6; i++){
        if(idbuf)
        {
            // *(idbuf + i) = NFC_READ_RAM_B(NFC_RAM0_BASE+i);
            *(idbuf + i) = readb(SUNXI_NFC_BASE + NFC_RAM0_BASE + i);
        }
        msg("%x ", readb(SUNXI_NFC_BASE + NFC_RAM0_BASE + i));
	}
    msg("\n");

    nfc_repeat_mode_disable();

	return ret;
}

#define NFC_CE_SEL				(7 << 24)
int  nand_get_id()
{
	int ret;
	__u8 i = 0;
	__u32 cfg;
    char idbuf[6] = {0};

	cfg = readl(SUNXI_NFC_BASE + NFC_CTL);
    cfg &= ((~NFC_CE_SEL) & 0xffffffff);
    cfg |= (0 << 24);
	writel(cfg, SUNXI_NFC_BASE + NFC_CTL);

	ret = nfc_get_id(idbuf);
    msg("nand id : ");
	for (i = 0; i < 6; i++){
        msg("%x ", idbuf[i]);
	}
    msg("\n");

	return ret;
}

int nfc_check_rb_ready(__u32 rb)
{
	int ret;
	__u32 cfg = readl(SUNXI_NFC_BASE + NFC_ST);


	cfg &= (NFC_RB_STATE0 << (rb & 0x3));

	if (cfg)
		ret = 0;
	else
		ret = -1;

	return ret;
}
__s32 nfc_select_rb(__u32 rb)
{
	__s32 cfg;

	cfg = readl(SUNXI_NFC_BASE + NFC_CTL);
    cfg &= ((~NFC_CE_SEL) & 0xffffffff);
    cfg |= ((rb & 0x1) << 3);
	writel(cfg, SUNXI_NFC_BASE + NFC_CTL);
    return 0;

}

int nfc_get_status(void)
{
	int ret;
	__s32 cfg;

    nfc_select_rb(0);
	nfc_repeat_mode_enable();

    cfg = 0;

    writel((readl(SUNXI_NFC_BASE + NFC_CTL)) & (~NFC_CTL_RAM_METHOD),
           SUNXI_NFC_BASE + NFC_CTL);
	writel(1, SUNXI_NFC_BASE + NFC_CNT);
	nand_exec_cmd(NFC_SEND_CMD1 | NFC_DATA_TRANS | 0x70);

	ret = nand_wait_cmd_fifo_empty();
	ret |= nand_wait_int();
	if(ret){
		return ret;
	}

    nfc_repeat_mode_disable();
    msg("nand status:%x \n", readb(SUNXI_NFC_BASE + NFC_RAM0_BASE));
    return 0;
}
int nand_read_test(void)
{
    int ret, i;
	u32 ecc_st;


    // writel(0x8003000f, 0x01c20080);
    // msg("nand clock:%x \n", readl(0x01c20060));
    // writel(0x1, 0x01c20060);
    // writel(0xffffffff, 0x01c20060);

    // msg("nand register: ");
	// for (i = 0; i < 20; i++){
    //     msg("[%x]%x ", 0x01c03000 + i * 4, readl(0x01c03000 + i * 4));
	// }
    // msg("\n");
    // writel(0x2, 0x01c03000); // Reset
    // msg("nand clear register: ");
	// for (i = 0; i < 20; i++){
    //     writel(0x0, 0x01c03000 + i * 4);
	// }
    // msg("\n");
    // writel(0x2, 0x01c03000); // Reset
    // udelay(2000);
    // writel(0x100, 0x01c0300c);
    // writel(0xff, 0x01c03010);
    // writel(0x800, 0x01c030a0);
    // writel(0x301, 0x01c03000);
    // writel(0x004000ff, 0x01c03024);

    // Data transfer
    writel(NFC_CTL_PAGE_SIZE(8192) | NFC_CTL_EN,
           SUNXI_NFC_BASE + NFC_CTL);
	writel(0x400, SUNXI_NFC_BASE + NFC_CNT);
    msg("nand CNT:%x \n", readl(SUNXI_NFC_BASE + NFC_CNT));
	writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
	       (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
	       (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET),
	       SUNXI_NFC_BASE + NFC_RCMD_SET);
	writel(1, SUNXI_NFC_BASE + NFC_SECTOR_NUM);

	writel(0, SUNXI_NFC_BASE + NFC_ADDR_LOW);
	writel(0, SUNXI_NFC_BASE + NFC_ADDR_HIGH);

    writel(0, SUNXI_NFC_BASE + NFC_ECC_CTL);

    writel((0x4a80 << 16) | (0x1 << 12) |
		       NFC_ECC_RANDOM_EN |
		       NFC_ECC_EN | NFC_ECC_PIPELINE,
		       SUNXI_NFC_BASE + NFC_ECC_CTL);
	nand_exec_cmd(NFC_RAW_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_SEND_CMD1 | NFC_SEND_CMD2 |
                  NFC_WAIT_FLAG | NFC_DATA_TRANS | NFC_SEND_ADDR |
                  (4 << NFC_ADDR_NUM_OFFSET));

	nand_exec_cmd(NFC_ECC_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_WAIT_FLAG |
                  NFC_DATA_TRANS);

    ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
    msg("nand ecc_st:%x \n", ecc_st);
    print_nand_register();
    msg("nand read: [%x]: ", SUNXI_NFC_BASE + NFC_RAM0_BASE);
	for (i = 0; i < 256; i++){
        msg("%x ", readl(SUNXI_NFC_BASE + NFC_RAM0_BASE + i * 4));
	}
    msg("\n");

    // Next sector
	nand_exec_cmd(NFC_RAW_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_WAIT_FLAG | NFC_DATA_TRANS);

	nand_exec_cmd(NFC_ECC_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_WAIT_FLAG |
                  NFC_DATA_TRANS);

    ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
    msg("nand ecc_st:%x \n", ecc_st);
    print_nand_register();
    msg("nand read2: [%x]: ", SUNXI_NFC_BASE + NFC_RAM0_BASE);
	for (i = 0; i < 256; i++){
        msg("%x ", readl(SUNXI_NFC_BASE + NFC_RAM0_BASE + i * 4));
	}
    msg("\n");
    // Page 1
	writel(0x10000, SUNXI_NFC_BASE + NFC_ADDR_LOW);
	writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
	       (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
	       (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET),
	       SUNXI_NFC_BASE + NFC_RCMD_SET);

	nand_exec_cmd(NFC_RAW_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_SEND_CMD1 | NFC_SEND_CMD2 |
                  NFC_WAIT_FLAG | NFC_DATA_TRANS | NFC_SEND_ADDR |
                  (4 << NFC_ADDR_NUM_OFFSET));

	nand_exec_cmd(NFC_ECC_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_WAIT_FLAG |
                  NFC_DATA_TRANS);

    ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
    msg("nand ecc_st:%x \n", ecc_st);

    msg("nand page 1 read: [%x]: ", SUNXI_NFC_BASE + NFC_RAM0_BASE);
	for (i = 0; i < 256; i++){
        msg("%x ", readl(SUNXI_NFC_BASE + NFC_RAM0_BASE + i * 4));
	}
    msg("\n");
    return ret;
}

int nand_random_read_test(void)
{
    int ret, i;
	u32 ecc_st;

    writel(NFC_CTL_PAGE_SIZE(8192) | NFC_CTL_EN,
           SUNXI_NFC_BASE + NFC_CTL);
	writel(0x400, SUNXI_NFC_BASE + NFC_CNT);
    msg("nand CNT:%x \n", readl(SUNXI_NFC_BASE + NFC_CNT));

	writel(1, SUNXI_NFC_BASE + NFC_SECTOR_NUM);
    writel(0, SUNXI_NFC_BASE + NFC_ECC_CTL);

    writel((0x4a80 << 16) | (0x1 << 12) |
		       NFC_ECC_RANDOM_EN |
		       NFC_ECC_EN | NFC_ECC_PIPELINE,
		       SUNXI_NFC_BASE + NFC_ECC_CTL);

    // load page
	writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
	       (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
	       (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET),
	       SUNXI_NFC_BASE + NFC_RCMD_SET);
	writel(0x0 , SUNXI_NFC_BASE + NFC_ADDR_LOW);
	writel(0 , SUNXI_NFC_BASE + NFC_ADDR_HIGH);
	nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
			     NFC_SEND_ADDR | NFC_WAIT_FLAG |
			     (4 << NFC_ADDR_NUM_OFFSET));
    // udelay(0xffff);
    // nand_wait_cmd_fifo_empty();
    // nfc_get_status();
    // writel((NFC_CMD_RNDOUTSTART),
	//        SUNXI_NFC_BASE + NFC_RCMD_SET);

	writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
	       (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
	       (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET),
	       SUNXI_NFC_BASE + NFC_RCMD_SET);
    msg("nand RCMD:%x \n", readl(SUNXI_NFC_BASE + NFC_RCMD_SET));
	writel(0x3fe, SUNXI_NFC_BASE + NFC_ADDR_LOW);

	// ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
	// 		    (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR |
	// 		    NFC_CMD_RNDOUT);
	nand_exec_cmd(NFC_RAW_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_SEND_CMD1 | NFC_SEND_CMD2 |
                  NFC_WAIT_FLAG | NFC_DATA_TRANS | NFC_SEND_ADDR |
                  (1 << NFC_ADDR_NUM_OFFSET) | 0x05);
    msg("nand ret:%d \n", ret);
    writel(0x400, SUNXI_NFC_BASE + NFC_CNT);
    // nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD);

	nand_exec_cmd(NFC_ECC_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_WAIT_FLAG |
                  NFC_DATA_TRANS);
    print_nand_register();
    ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
    msg("nand ecc_st:%x \n", ecc_st);

    msg("nand seek read: [%x]: ", SUNXI_NFC_BASE + NFC_RAM0_BASE);
	for (i = 0; i < 256; i++){
        msg("%x ", readl(SUNXI_NFC_BASE + NFC_RAM0_BASE + i * 4));
	}
    msg("\n");
    return ret;
}

int nand_read_test3(void)
{
    int ret, i;
	u32 ecc_st;

    writel(0x42e, SUNXI_NFC_BASE + NFC_ADDR_LOW);
	writel((NFC_CMD_RNDOUTSTART),
	       SUNXI_NFC_BASE + NFC_RCMD_SET);
    msg("nand RCMD:%x \n", readl(SUNXI_NFC_BASE + NFC_RCMD_SET));

	nand_exec_cmd(NFC_RAW_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_SEND_CMD1 | NFC_SEND_CMD2 |
                  NFC_WAIT_FLAG | NFC_DATA_TRANS | NFC_SEND_ADDR |
                  (1 << NFC_ADDR_NUM_OFFSET) | 0x05);

	nand_exec_cmd(NFC_ECC_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_WAIT_FLAG |
                  NFC_DATA_TRANS);

    ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
    msg("nand ecc_st:%x \n", ecc_st);

    msg("nand seek 2 read: [%x]: ", SUNXI_NFC_BASE + NFC_RAM0_BASE);
	for (i = 0; i < 256; i++){
        msg("%x ", readl(SUNXI_NFC_BASE + NFC_RAM0_BASE + i * 4));
	}
    msg("\n");
    return ret;
}

int nand_read_page_test(void)
{
    int ret, i, n;
	u32 ecc_st;

	__u32 addr_low = 0;
	__u32 addr_high = 0;
	__u32 row;
	__u32 column;
    char addr[5] = {0};
	// column = 512 * sector;
	// row = block * 2048 + page;
	column = 0;
	row = 2048;
	addr[0] = column & 0xff;
	addr[1] = (column >> 8) & 0xff;
	addr[2] = row & 0xff;
	addr[3] = (row >> 8) & 0xff;
	addr[4] = (row >> 16) & 0xff;


	for (i = 0; i < 5; i++){
		if (i < 4)
			addr_low |= (addr[i] << (i*8) );
		else
			addr_high |= (addr[i] << ((i - 4)*8));
	}

    msg("%s addr_low :%x !\n", __FUNCTION__, addr_low);
    msg("%s addr_high :%x !\n", __FUNCTION__, addr_high);
	// NFC_WRITE_REG(NFC_REG_ADDR_LOW, addr_low);
	// NFC_WRITE_REG(NFC_REG_ADDR_HIGH, addr_high);
    // Data transfer
    writel(NFC_CTL_PAGE_SIZE(8192) | NFC_CTL_EN,
           SUNXI_NFC_BASE + NFC_CTL);
	writel(0x400, SUNXI_NFC_BASE + NFC_CNT);
    msg("nand CNT:%x \n", readl(SUNXI_NFC_BASE + NFC_CNT));
	writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
	       (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
	       (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET),
	       SUNXI_NFC_BASE + NFC_RCMD_SET);
	writel(1, SUNXI_NFC_BASE + NFC_SECTOR_NUM);

	writel(0x2000000, SUNXI_NFC_BASE + NFC_ADDR_LOW);
	// writel(0x4 << 16, SUNXI_NFC_BASE + NFC_ADDR_LOW);
	// writel(2 << 24, SUNXI_NFC_BASE + NFC_ADDR_LOW);
	writel(0, SUNXI_NFC_BASE + NFC_ADDR_HIGH);

    writel(0, SUNXI_NFC_BASE + NFC_ECC_CTL);

    // writel((0x4a80 << 16) | (0x1 << 12) |
	// 	       NFC_ECC_RANDOM_EN |
	// 	       NFC_ECC_EN | NFC_ECC_PIPELINE,
	// 	       SUNXI_NFC_BASE + NFC_ECC_CTL);
    writel((0x4a80 << 16) | (0x1 << 12) |
		       NFC_ECC_RANDOM_EN |
		       NFC_ECC_EXCEPTION |
		       NFC_ECC_EN ,
		       SUNXI_NFC_BASE + NFC_ECC_CTL);
    // writel(0x4a801211, SUNXI_NFC_BASE + NFC_ECC_CTL);

	nand_exec_cmd(NFC_RAW_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_SEND_CMD1 | NFC_SEND_CMD2 |
                  NFC_WAIT_FLAG | NFC_DATA_TRANS | NFC_SEND_ADDR |
                  (4 << NFC_ADDR_NUM_OFFSET));

	nand_exec_cmd(NFC_ECC_CMD | NFC_DATA_SWAP_METHOD |
                  NFC_SEQ | NFC_WAIT_FLAG |
                  NFC_DATA_TRANS);

    ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
    msg("nand ecc_st:%x \n", ecc_st);
    print_nand_register();
    msg("nand read: [%x]: ", SUNXI_NFC_BASE + NFC_RAM0_BASE);
	for (i = 0; i < 256; i++){
        msg("%x ", readl(SUNXI_NFC_BASE + NFC_RAM0_BASE + i * 4));
	}
    msg("\n");
    for (n = 0; n < 6; ++n) {
        // Next sector
        nand_exec_cmd(NFC_RAW_CMD | NFC_DATA_SWAP_METHOD |
                    NFC_SEQ | NFC_WAIT_FLAG | NFC_DATA_TRANS);

        nand_exec_cmd(NFC_ECC_CMD | NFC_DATA_SWAP_METHOD |
                    NFC_SEQ | NFC_WAIT_FLAG |
                    NFC_DATA_TRANS);

        ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
        msg("nand ecc_st:%x \n", ecc_st);
        // msg("nand register: ");
        // for (i = 0; i < 20; i++){
        //     msg("[%x]%x ", 0x01c03000 + i * 4, readl(0x01c03000 + i * 4));
        // }
        // msg("\n");
        msg("nand read2: [%x]: ", SUNXI_NFC_BASE + NFC_RAM0_BASE);
        for (i = 0; i < 256; i++){
            msg("%x ", readl(SUNXI_NFC_BASE + NFC_RAM0_BASE + i * 4));
        }
        msg("\n");
    }

    return ret;
}
void print_nand_register(void)
{
    int i;
    msg("nand register: \n");
	for (i = 0; i < 20; i++){
        msg("[%x]%x ", 0x01c03000 + i * 4, readl(0x01c03000 + i * 4));
        if(i % 5 == 0)
            msg("\n");
	}
    msg("\n");
}
